import { Body, Controller, Delete, Get, HttpException, HttpStatus, Param, Patch, Post, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
import { Authorities } from 'src/decorators/authorities.decorator';
import { AuthoritiesGuard } from 'src/guards/authorities.guard';
import { Guard } from 'src/modules/guard/constants/guard.constant';
import { LantUtil } from '../../../../utils/lant.util';
import { BoolResDto } from '../../../common/dtos/bool.res.dto';
import { UuidReqDto } from '../../../common/dtos/uuid.req.dto';
import { UuidResDto } from '../../../common/dtos/uuid.res.dto';
import { GuardService } from '../../../guard/services/guard.service';
import { LogAppender } from '../../../log/constants/log.constant';
import { LogService } from '../../../log/services/log.service';
import { AuthService } from '../../../user/services/auth.service';
import { UserService } from '../../../user/services/user.service';
import { RoleType } from '../../constants/role.constant';
import { CreateRoleAdminReqDto } from '../../dtos/create-role.admin.req.dto';
import { GetRoleListAdminResDto } from '../../dtos/get-role-list.admin.res.dto';
import { UpdateRoleGuardListAdminReqDto } from '../../dtos/update-role-guard-list.admin.req.dto';
import { UpdateRoleAdminReqDto } from '../../dtos/update-role.admin.req.dto';
import { RoleService } from '../../services/role.service';
const pinyin = require('pinyin');

@ApiBearerAuth()
@ApiTags('roles')
@Controller('admin/roles')
export class RoleAdminController {
  constructor(
    private readonly userService: UserService,
    private readonly authService: AuthService,
    private readonly guardService: GuardService,
    private readonly roleService: RoleService,
    private readonly logService: LogService,
    private readonly lantUtil: LantUtil
  ) {}

  @ApiOperation({ summary: '新建用户角色' })
  @ApiOkResponse({ description: '返回新增的角色Uuid', type: UuidResDto })
  @Authorities(Guard.RoleManage)
  @UseGuards(AuthGuard(), AuthoritiesGuard)
  @Post()
  async create(@Body() { name, guard_uuid_list, description }: CreateRoleAdminReqDto): Promise<UuidResDto> {
    try {
      const count = await this.roleService.checkDuplicateByName(name);
      if (count > 0) throw new HttpException(`${name}已存在，不能重复创建`, HttpStatus.BAD_REQUEST);

      const role = await this.roleService.create(name, guard_uuid_list, description);

      return { uuid: role.uuid };
    } catch (error) {
      if (error instanceof HttpException) throw error;
      this.logService.error(LogAppender.role, `Cannot create role by ${[name, guard_uuid_list]}, the error is ${error}`, this.lantUtil.parseError(error));
      throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @ApiOperation({ summary: '删除用户角色' })
  @ApiOkResponse({ description: '返回删除是否成功', type: BoolResDto })
  @Authorities(Guard.RoleManage)
  @UseGuards(AuthGuard(), AuthoritiesGuard)
  @Delete(':uuid')
  async delete(@Param() { uuid }: UuidReqDto): Promise<BoolResDto> {
    try {
      const userRole = await this.roleService.findOneByUuid(uuid);
      if (!userRole) throw new HttpException('找不到指定角色', HttpStatus.BAD_REQUEST);
      if (userRole.type == RoleType.system) throw new HttpException('不能删除系统角色', HttpStatus.BAD_REQUEST);
      const userCount = await this.userService.findCountByRoleUuid(uuid);
      if (userCount > 0) throw new HttpException(`${userRole.name}下存在用户，不能删除`, HttpStatus.BAD_REQUEST);

      const deleteResult = await this.roleService.delete(uuid);

      return { result: !!deleteResult.affected };
    } catch (error) {
      if (error instanceof HttpException) throw error;
      this.logService.error(LogAppender.area, `Cannot delete area by ${uuid}, the error is ${error}`, this.lantUtil.parseError(error));
      throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @ApiOperation({ summary: '更改角色权限列表' })
  @ApiOkResponse({ description: '更改结果', type: BoolResDto })
  @Authorities(Guard.RoleManage)
  @UseGuards(AuthGuard(), AuthoritiesGuard)
  @Patch(':uuid/guards')
  async updateGuardList(@Param() { uuid }: UuidReqDto, @Body() { guard_uuid_list }: UpdateRoleGuardListAdminReqDto): Promise<BoolResDto> {
    try {
      const userRole = await this.roleService.findOneByUuid(uuid);
      if (!userRole) throw new HttpException('找不到指定角色', HttpStatus.BAD_REQUEST);
      if (!guard_uuid_list) throw new HttpException('权限列表不能为空', HttpStatus.BAD_REQUEST);

      const updateResult = await this.roleService.update(uuid, undefined, undefined, guard_uuid_list);
      const guardList = await this.guardService.findAllByUuidList(guard_uuid_list);
      const guardNameList = guardList.map(guard => guard.name);

      //1. 找到该角色下的所有用户
      const userList = await this.userService.findAllByRoleUuid(userRole.uuid);
      for (const user of userList) {
        //2. 给每个用户更新token
        await this.authService.updateToken(user.uuid, user.jobId, guardNameList);
      }

      return { result: !!updateResult.affected };
    } catch (error) {
      if (error instanceof HttpException) throw error;
      this.logService.error(LogAppender.role, `Cannot update role by ${JSON.stringify({ uuid, guard_uuid_list })}, the error is ${error}`, this.lantUtil.parseError(error));
      throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @ApiOperation({ summary: '更改角色信息' })
  @ApiOkResponse({ description: '角色信息更改结果', type: BoolResDto })
  @Authorities(Guard.RoleManage)
  @UseGuards(AuthGuard(), AuthoritiesGuard)
  @Patch(':uuid')
  async update(@Param() { uuid }: UuidReqDto, @Body() body: UpdateRoleAdminReqDto): Promise<BoolResDto> {
    try {
      const userRole = await this.roleService.findOneByUuid(uuid);
      if (!userRole) throw new HttpException('找不到指定角色', HttpStatus.BAD_REQUEST);

      if (body.name) {
        const count = await this.roleService.checkDuplicateByName(body.name, uuid);
        if (count > 0) throw new HttpException(`${body.name}已存在，不能重复创建`, HttpStatus.BAD_REQUEST);
      }

      const updateResult = await this.roleService.update(uuid, body.name, body.description);

      return { result: !!updateResult.affected };
    } catch (error) {
      if (error instanceof HttpException) throw error;
      this.logService.error(LogAppender.role, `Cannot update role by ${JSON.stringify(body)}, the error is ${error}`, this.lantUtil.parseError(error));
      throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @ApiOperation({ summary: '后台获取所有用户角色列表' })
  @ApiOkResponse({ description: '返回用户角色列表', type: GetRoleListAdminResDto, isArray: true })
  @Authorities(Guard.RoleManage, Guard.RoleView)
  @UseGuards(AuthGuard(), AuthoritiesGuard)
  @Get()
  async getAll(): Promise<GetRoleListAdminResDto[]> {
    try {
      const roleList = await this.roleService.find();
      return roleList.map(role => {
        const guardList = role.guardList?.sort(function compareFunction(current, next) {
          return pinyin.compare(current.name, next.name);
        });
        return {
          uuid: role.uuid,
          name: role.name,
          description: role.description,
          type: role.type,
          guard_list: guardList?.map(guard => {
            return {
              uuid: guard.uuid,
              name: guard.displayName
            };
          })
        };
      });
    } catch (error) {
      if (error instanceof HttpException) throw error;
      this.logService.error(LogAppender.role, `Cannot get roleList, the error is ${error}`, this.lantUtil.parseError(error));
      throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @ApiOperation({ summary: '后台根据角色获取所有权限列表' })
  @ApiOkResponse({ description: '返回用户角色列表', type: GetRoleListAdminResDto })
  @Authorities(Guard.RoleManage, Guard.RoleView)
  @UseGuards(AuthGuard(), AuthoritiesGuard)
  @Get(':uuid')
  async getListByRole(@Param() { uuid }: UuidReqDto): Promise<GetRoleListAdminResDto> {
    try {
      const role = await this.roleService.findGuardListByRole(uuid);

      return {
        uuid: role.uuid,
        name: role.name,
        description: role.description,
        type: role.type,
        guard_list: role.guardList?.sort().map(guard => {
          return {
            uuid: guard.uuid,
            name: guard.displayName
          };
        })
      };
    } catch (error) {
      if (error instanceof HttpException) throw error;
      this.logService.error(LogAppender.role, `Cannot get role guard list, the error is ${error}`, this.lantUtil.parseError(error));
      throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }
}
